package switchviz.network.snmp;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.SMIConstants;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;

import switchviz.core.data.TimeStamp;
import switchviz.network.NetworkData;

public class SnmpPdu implements ResponseListener
{
	private static Logger logger = Logger.getLogger(SnmpPdu.class);
	
	private PDU _pdu = null;
	private List<NetworkData> _datas = new ArrayList<NetworkData>();
	
	public SnmpPdu(PDU pdu) throws NullPointerException
	{
		if (pdu == null)
			throw new NullPointerException();
		
		_pdu = pdu;
	}
	
	public PDU getPdu()
	{
		return _pdu;
	}
	
	public int size()
	{
		return _pdu.size();
	}
	
	public void add(NetworkData data)
	{
		if(data == null) return;
		_datas.add(data);
		_pdu.add(new VariableBinding(new OID(data.getOid())));
	}
	
	public static Object processValue(Variable variable)
	{
		Object value = null;
	
		switch(variable.getSyntax())
		{
		case SMIConstants.SYNTAX_COUNTER32:
		case SMIConstants.SYNTAX_TIMETICKS:
		case SMIConstants.SYNTAX_GAUGE32:
			value = variable.toLong();
			break;
		case SMIConstants.SYNTAX_INTEGER32:
			value = variable.toInt();
			break;
		case SMIConstants.SYNTAX_OCTET_STRING:
			value = variable.toString();
			break;
		case SMIConstants.SYNTAX_NULL:
			value = null;
			break;
		case SMIConstants.SYNTAX_OBJECT_IDENTIFIER:
			value = variable.toString();
			break;
		default:
			
			break;
		}
		
		return value;
	}
	
	public void onResponse(ResponseEvent responseEvent)
	{		
		((Snmp)responseEvent.getSource()).cancel(responseEvent.getRequest(), this);
		
		PDU response = responseEvent.getResponse();

		SnmpStatus status = null;
		
		if(responseEvent.getUserObject().getClass().equals(SnmpStatus.class))
			status = (SnmpStatus)responseEvent.getUserObject();
		
		if(status != null)
			status.updateStatus(SnmpStatus.Status.Data);
		
		if (responseEvent.getResponse() == null)
		{
			logger.error("Request timed out.");
			if(status != null)
				status.updateStatus(SnmpStatus.Status.TimedOut);
			return;
		}
		
		if (logger.isDebugEnabled())
			logger.debug("Pdu received.");
		if(status != null)
			status.updateStatus(SnmpStatus.Status.Received);
		
		TimeStamp timeStamp = TimeStamp.generate();
		
		for (int i = 0; i < response.size(); ++i)
		{
			Object value = null;
			Variable variable = response.get(i).getVariable();
			
			if (variable.isException() == false)
			{
				value = processValue(variable);
				if(value == null)
				{
					logger.warn("Received variable with unsupported data type." 
						+ "\n\t oid=" + response.get(i).getOid().toString()
						+ " syntax=" + variable.getSyntaxString() + " (" + variable.getSyntax() + ")");
				}
			}
			else
			{
				logger.error("Received variable with exception."
					+ "\n\t oid=" + response.get(i).getOid().toString()
					+ " syntax=" + variable.getSyntaxString() + " (" + variable.getSyntax() + ")");
			}

			// todo: set status for adaptor by userobject
			_datas.get(i).setValue(value, timeStamp);
		}
	}
}
